#ifndef _CAMERA_H_
#define _CAMERA_H_
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <SERender/Drive/Drive.h>
#include <SECore/Reflect.h>
#include <glm/gtc/quaternion.hpp>


namespace SEngine
{
    enum CameraType 
    {
        Perspective,
        Ortho
    };

    class Camera 
    {
        REF_ENABLE

    public:
        Camera() = default;
        virtual ~Camera() = default;
        
        Camera & Translate(glm::vec3 translate);
        Camera & Rotate(glm::quat rotate, bool fixUp = true, glm::vec3 fixedUp = glm::vec3{0.0f, 1.0f, 0.0f});
        Camera & SetPos(glm::vec3 pos) { m_pos = pos; return *this; }
        Camera & SetFront(glm::vec3 front) { m_front = front; return *this; }
        Camera & SetUp(glm::vec3 up) { m_up = up; return *this; }
        Camera & SetFov(float fov) { m_fov = fov; return *this; }
        Camera & SetSize(float size) { m_size = size; return *this; }
        Camera & SetType(CameraType type) { m_type = type; return *this; }
        Camera & SetZNear(float zNear) { m_zNear = zNear; return *this; }
        Camera & SetZFar(float zFar) { m_zFar = zFar; return *this; }
        Camera & SetClearColor(glm::vec4 color) { m_clearColor = color; return *this; }
        Camera & SetClearBuffer(int en) { m_clearBuffer = en; return *this; }
        
        glm::vec4 & GetClearColor() { return m_clearColor; }
        int GetClearBuffer() const { return m_clearBuffer; }
        float & GetFov() { return m_fov; }
        float & GetSize() { return m_size; }
        float & GetZNear() { return m_zNear; }
        float & GetZFar() { return m_zFar; }
        glm::vec3 & GetPos() { return m_pos; }
        glm::vec3 & GetFront() { return m_front; }
        glm::vec3 & GetUp() { return m_up; }
        // glm::vec3 GetUp() { 
        //     auto right = glm::normalize(glm::cross(m_front, glm::vec3{0.0f, 1.0f, 0.0f}));
        //     return glm::normalize(glm::cross(right, m_front));
        // }

        glm::mat4 GetViewMatrix();
        glm::mat4 GetProjectMatrix(int w, int h) const;

        CameraType GetType() { return (CameraType)m_type; }

    private:
        glm::vec3 m_pos         = glm::vec3{0.0f};
        glm::vec3 m_front       = glm::vec3{1.0f, 0.0f, 0.0f};
        glm::vec3 m_up          = glm::vec3{0.0f, 1.0f, 0.0f};
        glm::quat m_rotate      = glm::quat{glm::vec3{0.0f}};
        float m_fov             = 45.0f;
        float m_size            = 5.0f;
        float m_zNear           = 0.01f;
        float m_zFar            = 100.0f;           
        glm::vec4 m_clearColor  = glm::vec4{0.1f, 0.1f, 0.1f, 1.0f};
        int m_clearBuffer       = DEPTH_BUFFER_BIT | COLOR_BUFFER_BIT;
        int m_type              = Perspective;
    };

    REF_REGISTER_CLASS(Camera, 
        REF_REGISTER_FIELD(Camera, m_pos),
        REF_REGISTER_FIELD(Camera, m_front),
        REF_REGISTER_FIELD(Camera, m_up),
        REF_REGISTER_FIELD(Camera, m_rotate),
        REF_REGISTER_FIELD(Camera, m_fov),
        REF_REGISTER_FIELD(Camera, m_size),
        REF_REGISTER_FIELD(Camera, m_zNear),
        REF_REGISTER_FIELD(Camera, m_zFar),
        REF_REGISTER_FIELD(Camera, m_clearColor),
        REF_REGISTER_FIELD(Camera, m_clearBuffer),
        REF_REGISTER_FIELD(Camera, m_type),
        REF_REGISTER_FUNC(Camera, SetPos),
        REF_REGISTER_FUNC(Camera, GetPos),
        REF_REGISTER_FUNC(Camera, SetFront),
        REF_REGISTER_FUNC(Camera, GetFront),
        REF_REGISTER_FUNC(Camera, GetUp)
    )
} // namespace SEngine


#endif